package com.derbysoft.ota.application.translator.rs;

import com.derbysoft.ota.application.translator.MetaDatas;
import com.derbysoft.ota.application.util.Dates;
import com.derbysoft.ota.application.util.Elements;
import com.derbysoft.ota.interfaces.protocol._2009a.TPAExtensionsType;
import com.derbysoft.remote.dswitch.generic.dto.RateRangeDTO;
import com.derbysoft.remote.share.Currency;
import com.google.common.base.Function;
import com.googlecode.functionalcollections.FunctionalIterables;
import org.apache.commons.collections.CollectionUtils;

import com.derbysoft.modules.eip.translator.Translator;
import com.derbysoft.ota.interfaces.protocol._2009a.PropertyValueMatchType;
import com.derbysoft.remote.dswitch.hotel.dto.ProviderRateInfoDTO;
import com.derbysoft.remote.dswitch.hotel.dto.RateRangeInfoDTO;

import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

public class RateRangeTranslator
		implements Translator<ProviderRateInfoDTO, PropertyValueMatchType.RateRange> {

	@Override
	public PropertyValueMatchType.RateRange translate(ProviderRateInfoDTO source) {
		RateRangeInfoDTO dto = assembleRateInfo(source);
		if (dto == null) {
			return null;
		}
		PropertyValueMatchType.RateRange range = new PropertyValueMatchType.RateRange();
		range.setCurrencyCode(dto.getRateRange().getCurrency().getCode());
		range.setMaxRate(dto.getRateRange().getMaxRate());
		range.setMinRate(dto.getRateRange().getMinRate());
		range.setInfoSource(source.getProviderId().getId());
		//put timestamp to TPAExtensions, timestamp should be the latest time of them
		range.setTPAExtensions(new TPAExtensionsType());
		range.getTPAExtensions().getAny().add(Elements.of(MetaDatas.RATE_RANGE_TIME_STAMP, Dates.format(dto.getTimestamp())));
		return range;
	}

	private RateRangeInfoDTO assembleRateInfo(ProviderRateInfoDTO source) {
		//Assemble rate range info. if the currency is different, return the first rate range,
		//else rate range is the max and min value of all ranges
		List<RateRangeInfoDTO> rateRangeInfos = source.getRateRangeInfos();
		if (CollectionUtils.isEmpty(rateRangeInfos)) {
			return null;
		}

		if (currencyIsDifferent(rateRangeInfos)) {
			return rateRangeInfos.iterator().next();
		}

		RateRangeInfoDTO rateRangeInfoDTO = new RateRangeInfoDTO();
		rateRangeInfoDTO.setRateRange(rateRangeDTO(rateRangeInfos));
		rateRangeInfoDTO.setTimestamp(lastTimestamp(rateRangeInfos));
		return rateRangeInfoDTO;
	}

	private Date lastTimestamp(List<RateRangeInfoDTO> rateRangeInfos) {
		Collections.sort(rateRangeInfos, new Comparator<RateRangeInfoDTO>() {
			public int compare(RateRangeInfoDTO o1, RateRangeInfoDTO o2) {
				return o2.getTimestamp().compareTo(o1.getTimestamp());
			}
		});
		return rateRangeInfos.iterator().next().getTimestamp();
	}

	private RateRangeDTO rateRangeDTO(List<RateRangeInfoDTO> rateRangeInfos) {
		RateRangeDTO dto = new RateRangeDTO();
		dto.setCurrency(rateRangeInfos.iterator().next().getRateRange().getCurrency());
		dto.setMaxRate(findMaxRate(rateRangeInfos));
		dto.setMinRate(findMinRate(rateRangeInfos));
		return dto;
	}

	private BigDecimal findMaxRate(List<RateRangeInfoDTO> rateRangeInfos) {
		Collections.sort(rateRangeInfos, new Comparator<RateRangeInfoDTO>() {
			public int compare(RateRangeInfoDTO o1, RateRangeInfoDTO o2) {
				return o2.getRateRange().getMaxRate().compareTo(o1.getRateRange().getMaxRate());
			}
		});
		return rateRangeInfos.iterator().next().getRateRange().getMaxRate();
	}

	private BigDecimal findMinRate(List<RateRangeInfoDTO> rateRangeInfos) {
		Collections.sort(rateRangeInfos, new Comparator<RateRangeInfoDTO>() {
			public int compare(RateRangeInfoDTO o1, RateRangeInfoDTO o2) {
				return o1.getRateRange().getMinRate().compareTo(o2.getRateRange().getMinRate());
			}
		});
		return rateRangeInfos.iterator().next().getRateRange().getMinRate();
	}

	private boolean currencyIsDifferent(List<RateRangeInfoDTO> rateRangeInfos) {
		return FunctionalIterables.make(rateRangeInfos).map(new Function<RateRangeInfoDTO, Currency>() {
			@Override
			public Currency apply(@Nullable RateRangeInfoDTO from) {
				return from.getRateRange().getCurrency();
			}
		}).toSet().size() > 1;
	}
}
